<template>
	<scroll-view 
		class="virtual-list" 
		ref="listContainer" 
		
		@scroll="scroll"
		scroll-y
	>
		<view class="virtual-list-box" :style="{ 'height': `${ totalHeight }px`}">
			<view class="virtual-list-orientation" :style="{ 'top': `${visibleItems[0]?.offset}px` }">
				<view
					class="virtual-list-item" 
					v-for="item in visibleItems"
					:style="{ 'height': `${item.size}px` }"
				>{{ item.index }}</view>
			</view>
		</view>
	</scroll-view>
</template>

<script setup>
// https://blog.csdn.net/qiwoo_weekly/article/details/134301493
import { getCurrentInstance, ref, computed, onMounted,reactive } from 'vue'
const listContainer = ref(null);
const finialStartIndex = ref(0);
const visibleItems = ref([]);
const itemCount = ref(100);
const totalHeight = ref(0);

//缓存item的高度和当时的index
const measuredData = reactive({
	measuredDataMap: {},
	LastMesuredItemIndex: -1
});

//计算预计的总高度
const predictHeight = (size) => {
	let measuredHeight = 0;
	if(measuredData.LastMesuredItemIndex >= 0) {
		const lastMeasuredItem = measuredData.measuredDataMap[measuredData.LastMesuredItemIndex];
		measuredHeight = lastMeasuredItem.offset + lastMeasuredItem.size;
	}
	//未计算高度的项数
	const unMeasuredItemsCount = itemCount.value - measuredData.LastMesuredItemIndex - 1;
	//预测高度
	const totalEstimatedHeight = measuredHeight + unMeasuredItemsCount * size;
	return totalEstimatedHeight;
}

//获取指定的index数据
const getItemMetaData = (index) => {
	if(index > measuredData.LastMesuredItemIndex) {
		let offset = 0;
		if(measuredData.LastMesuredItemIndex >= 0) {
			const lastMeasuredItem = measuredData.measuredDataMap[measuredData.LastMesuredItemIndex];
			offset += lastMeasuredItem.offset + lastMeasuredItem.size;
		}

		for(let i = measuredData.LastMesuredItemIndex + 1; i <= index; i++) {
			// const currentItemSize = i % 2 == 0 ? 50 : 30;
			const currentItemSize = Math.random() * 100;
			measuredData.measuredDataMap[i] = { size: currentItemSize, offset,index: i };
			offset += currentItemSize;
		}
		measuredData.LastMesuredItemIndex = index;
	}
	return measuredData.measuredDataMap[index];
}

//获取初始索引
const getStartIndex = (scrollOffset) => {
	let index = 0;
	while(true) {
		const currentOffset = getItemMetaData(index).offset;
		if(currentOffset >= scrollOffset) return index;
		if(index >= itemCount.value) return itemCount.value;
		index++;
	}
}

//获取最后的结束索引
const getEndIndex = (startIndex) => {
	const startItem = getItemMetaData(startIndex);
	const maxOffset = startItem.offset + 200;
	let offset = startItem.offset + startItem.size;
	let endIndex = startIndex;
	while(offset <= maxOffset && endIndex < (itemCount.value - 1)) {
		endIndex++;
		const currentItem = getItemMetaData(endIndex);
		offset += currentItem.size;
	}
	return endIndex;
}


const app = getCurrentInstance();
const query = uni.createSelectorQuery().in(app);
onMounted(()=>{
	totalHeight.value = predictHeight(100);
	query.select('.virtual-list').boundingClientRect(data=>{
		const startIndex = getStartIndex(0);
		finialStartIndex.value = startIndex;
	
		const endIndex = getEndIndex(startIndex);
		visibleItems.value = [];
		for(let i = startIndex; i < endIndex;i++) {
			visibleItems.value.push(measuredData.measuredDataMap[i]);
		}
	}).exec();
})

const scroll = (e) => {
	// const startIndex = Math.floor(e.detail.scrollTop/30);
	// finialStartIndex.value = Math.max(0, startIndex - 2);
	// const numVisible = Math.ceil(200/30);
	// const endIndex = Math.min(100, startIndex + numVisible + 2);
	// visibleItems.value = [];
	// for(let i = finialStartIndex.value; i < endIndex;i++) {
	// 	visibleItems.value.push(i);
	// }
	const startIndex = getStartIndex(e.detail.scrollTop);
	finialStartIndex.value = startIndex;
	const endIndex = getEndIndex(startIndex);
	visibleItems.value = [];
	for(let i = startIndex; i <= endIndex;i++) {
		visibleItems.value.push(measuredData.measuredDataMap[i]);
	}
	// console.log(measuredData.measuredDataMap[finialStartIndex.value].offset);
	// console.log(visibleItems.value);
	totalHeight.value = predictHeight(100);
}
</script>

<style lang="scss">
.virtual-list {
	overflow-y: auto;
	height: 200px;
	border: 1rpx solid #ddd;
	.virtual-list-box {
		position: relative;
		.virtual-list-orientation {
			display: flex;
			flex-direction: column;
			position: absolute;
			.virtual-list-item{
				height: 30px;
				top: 0;
				padding: 0 10rpx;
				box-sizing: border-box;
				border-bottom: 1rpx solid pink;
			}
		}	
	}
}
</style>